/**
 * 单行 item 数量
 */
const LineCount = 9

/**
 * 开始倒计时 s
 */
const SatrtCountdownSecond = 3

/**
 * 游戏状态
 */
enum GAME_STATUS {

    INIT,

    RUNNING,

    END,

}

/**
 * Mahjong 信息
 */
const Mahjong = {

    /**
     * 类型: 万 筒 索
     */
    Type: ['w', 't', 's'],

    /**
     * 类型牌张数
     */
    SingleCount: 9,

    /**
     * 有几张一样的牌
     */
    SingleRepeat: 4,

    /**
     * 总共牌数
     */
    TotalCount: 108,

}

/**
 * 游戏数据
 */
class GameInfo {

    status: GAME_STATUS

    cardScale: number

    data: Array<string>

    sourceData: Array<string>

    selectCards: Array<cc.Node>

    playing: boolean

    totalTime: number

    tipCards: Map<string, number>

}

const { ccclass, property } = cc._decorator;

@ccclass
export default class Game extends cc.Component {

    @property(cc.Node)
    cardsContent: cc.Node = null

    @property(cc.Node)
    cardItem: cc.Node = null

    @property(cc.Node)
    winParticle: cc.Node = null

    @property(cc.Node)
    startCountdown: cc.Node = null

    @property(cc.Node)
    timer: cc.Node = null

    @property(cc.Node)
    tipCardsContent: cc.Node = null

    @property(cc.Node)
    tipCardItem: cc.Node = null

    _gameInfo: GameInfo
    tt: any
    onLoad() {

        //@ts-ignore
        window.aaa = this

        this.tt = () => { this.timer.getComponent(cc.Label).string = `${++this._gameInfo.totalTime}s` }

        this._gameInfo = new GameInfo()

        /**
         * 计算 cardItem 比例
         */
        this._gameInfo.cardScale = Math.floor((this.cardsContent.width / LineCount) / this.cardItem.width * 100) / 100

        this._gameInfo.data = []

        this._gameInfo.sourceData = []

        /**
         * 创建 cardItem
         */
        this.cardsContent.removeAllChildren()

        for (let i = 0; i < Mahjong.TotalCount; i++) {

            let cloneCardItem = cc.instantiate(this.cardItem)

            cloneCardItem.width = cloneCardItem.width * this._gameInfo.cardScale

            cloneCardItem.height = cloneCardItem.height * this._gameInfo.cardScale

            cloneCardItem.idx = i

            this._gameInfo.sourceData.push(`${Mahjong.Type[Math.floor(i / (Mahjong.SingleRepeat * Mahjong.SingleCount))]}${i % Mahjong.SingleCount + 1}`)

            cloneCardItem.parent = this.cardsContent
        }

        /**
         * 创建提示Card
         */
        this.tipCardsContent.removeAllChildren()

        this._gameInfo.tipCards = new Map()

        for (let i = 0; i < Mahjong.Type.length; i++) {

            for (let j = 1; j <= Mahjong.SingleCount; j++) {

                let val = `${Mahjong.Type[i]}${j}`

                if (!this._gameInfo.tipCards[val]) {

                    this._gameInfo.tipCards[val] = Mahjong.SingleRepeat

                }

                let cloneTipCardItem = cc.instantiate(this.tipCardItem)

                cloneTipCardItem.val = val

                cloneTipCardItem.parent = this.tipCardsContent

            }

        }

    }

    onEnable() {

        this.init()

    }

    onDisable() {

        /**
         * 重置游戏状态
         */
        this._gameInfo.status = GAME_STATUS.INIT

        /**
         * 取消计时
         */
        this.endTiming()

        /**
         * 删除选中节点动画
         */
        while (this._gameInfo.selectCards.length) {

            this.endCardAction(this._gameInfo.selectCards.pop())

        }

    }

    init() {

        /**
         * 初始化数据
         */
        this._gameInfo.status = GAME_STATUS.INIT

        this._gameInfo.playing = false

        this._gameInfo.totalTime = 0

        this._gameInfo.selectCards = []

        this._gameInfo.data = ut.Clone(this._gameInfo.sourceData)

        for (let i in this._gameInfo.tipCards) {

            this._gameInfo.tipCards[i] = Mahjong.SingleRepeat

        }

        this.timer.getComponent(cc.Label).string = `0s`

        this.shuffle()

        this.setupCards()

        this.setupTipCards()

        this.hideCards()

        /**
         * 开始流程
         */
        this.startCountdownAnim(SatrtCountdownSecond)

    }

    /**
     * 洗牌
     */
    shuffle() {

        for (var i = 0; i < this._gameInfo.data.length; i++) {

            let r = ut.RandomInt(0, this._gameInfo.data.length)

            if (i != r) {
                let v = ut.Clone(this._gameInfo.data[i])

                this._gameInfo.data[i] = this._gameInfo.data[r]

                this._gameInfo.data[r] = v
            }
        }

    }

    /**
     * 开始游戏显示牌面动画
     */
    showCardsAnim() {

        this._gameInfo.playing = true

        let delayTime = 0

        for (let i = 0; i < this.cardsContent.children.length; i++) {

            let cardItem = this.cardsContent.children[i]

            let hide = cardItem.PathChild('cardHide')

            hide.opacity = 255

            hide.active = true

            let show = cardItem.PathChild('cardShow')

            show.opacity = 0

            show.active = true

            cc.tween(cardItem)
                .delay(delayTime)
                .call(() => {
                    cc.tween(hide).to(0.5, { opacity: 0 }).start()
                    cc.tween(show).to(0.5, { opacity: 255 }).start()
                })
                .delay(0.5)
                .call(() => {
                    if (i >= this.cardsContent.children.length - 1) {
                        this._gameInfo.playing = false
                    }
                })
                .start()

            if (i != 0 && i % LineCount == LineCount - 1) { delayTime += 0.1 }

        }

    }

    /**
     * 开始游戏隐藏牌面
     */
    hideCards() {

        for (let i = 0; i < this.cardsContent.children.length; i++) {

            let cardItem = this.cardsContent.children[i]

            cardItem.PathChild('cardHide').active = true

            cardItem.PathChild('cardShow').active = false

            cardItem.PathChild('cardHide').opacity = 255

            cardItem.PathChild('cardShow').opacity = 255
        }

    }

    /**
     * 刷新牌面
     */
    setupCards() {

        for (let i = 0; i < this.cardsContent.children.length; i++) {

            let cardItem: any = this.cardsContent.children[i]

            cardItem.idx = i

            cardItem.val = this._gameInfo.data[i]

            cardItem.PathChild('cardHide').active = false

            if (cardItem.val == null) {

                cardItem.PathChild('cardShow').active = false

            } else {

                ut.SetSpriteFrame(`val/${cardItem.val}`, cardItem.PathChild('cardShow/val', cc.Sprite))

                cardItem.PathChild('cardShow').active = true

            }

        }

    }

    /**
     * 刷新提示牌张数
     */
    setupTipCards() {

        for (let i = 0; i < this.tipCardsContent.children.length; i++) {

            let tipCardItem: any = this.tipCardsContent.children[i]

            ut.SetSpriteFrame(`val/${tipCardItem.val}`, tipCardItem.PathChild('frame/val', cc.Sprite))

            tipCardItem.PathChild('count', cc.Label).string = `X${this._gameInfo.tipCards[tipCardItem.val]}`

        }

    }

    /**
     * 开始游戏倒计时
     * @param second 秒
     * @returns 
     */
    startCountdownAnim(second) {

        if (this._gameInfo.status != GAME_STATUS.INIT) { return }

        if (second < 0) {

            this.startCountdown.getComponent(cc.Label).string = ''

            this._gameInfo.status = GAME_STATUS.RUNNING

            this._gameInfo.playing = false

            this.scheduleOnce(() => {

                this.startTiming()

            }, 1)

            this.showCardsAnim()

            return
        }

        let self = this

        this._gameInfo.playing = true

        cc.tween(this.startCountdown)
            .call(() => { self.startCountdown.getComponent(cc.Label).string = `${second}` })
            .to(0.2, { scale: 1.1 })
            .to(0.1, { scale: 1 })
            .delay(0.7)
            .call(() => { self.startCountdownAnim(--second) })
            .start()

    }

    /**
     * 开始选中动画
     */
    startCardAction() {

        for (let i = 0; i < this._gameInfo.selectCards.length; i++) {

            let cardItem = this._gameInfo.selectCards[i]

            cardItem.stopAllActions()

            cardItem.opacity = 255

            cc.tween(cardItem)
                .repeat(5, cc.sequence(cc.fadeOut(0.5), cc.fadeIn(0.5)))
                .start()

        }

    }

    /**
     * 取消选中动画
     * @param cc.Node 
     */
    endCardAction(cardItem) {

        cardItem.stopAllActions()

        cardItem.opacity = 255

    }

    /**
     * 错误动画
     */
    errorCardAction() {

        while (this._gameInfo.selectCards.length) {

            let cardItem = this._gameInfo.selectCards.shift()

            this.endCardAction(cardItem)

            let node = cardItem.PathChild('cardShow')

            node.stopAllActions()

            node.opacity = 255

            cc.tween(node)
                .repeat(3, cc.sequence(cc.moveBy(0.1, -3, 0), cc.moveBy(0.1, 3, 0)))
                .start()

        }

    }

    startTiming() {

        this.schedule(this.tt, 1)

    }

    endTiming() {

        this.unschedule(this.tt)

    }

    /**
     * 尝试消除
     * @returns true | false
     */
    tryDispel() {

        let select0 = this._gameInfo.selectCards[0]

        let select1 = this._gameInfo.selectCards[1]

        if (select0.val != select1.val) {

            return false

        }

        // 至少一边通畅
        if ((this.checkUp(select0.idx) || this.checkDown(select0.idx)) && (this.checkUp(select1.idx) || this.checkDown(select1.idx))) {

            return true

        }

        // 相邻牌
        if (Math.abs(select0.idx - select1.idx) == LineCount && (this.checkUp(select0.idx) || this.checkDown(select0.idx) || this.checkUp(select1.idx) || this.checkDown(select1.idx))) {

            return true

        }

        return false
    }

    /**
     * 检查上面是否通畅
     * @returns true | false
     */
    checkUp(idx) {

        for (let i = idx - LineCount; i >= 0; i -= LineCount) {

            if (this.cardsContent.children[i] && this.cardsContent.children[i].val != null) {

                return false

            }

        }

        return true

    }

    /**
     * 检查下面是否通畅
     * @returns true | false
     */
    checkDown(idx) {

        for (let i = idx + LineCount; i < this.cardsContent.children.length; i += LineCount) {

            if (this.cardsContent.children[i] && this.cardsContent.children[i].val != null) {

                return false

            }

        }

        return true

    }

    /**
     * 检查游戏结束
     * @returns true | false
     */
    gameOver() {

        for (var i = 0; i < this.cardsContent.children.length; i++) {

            if (this.cardsContent.children[i].val != null) {

                return false
            }
        }

        return true

    }

    /**
     * 保存到排行榜
     */
    saveResult() {

        let recordData = ut.GetLocalStorage('wlffl-record') || []

        recordData.push({ consume: this._gameInfo.totalTime, date: new Date().Format('yyyy-mm-dd') })

        recordData.sort((a, b) => { return a.consume - b.consume })

        while (recordData.length > 30) {

            recordData.pop()

        }

        ut.SetLocalStorage('wlffl-record', recordData)

    }

    btnCardItem(e) {

        if (this._gameInfo.status != GAME_STATUS.RUNNING) {

            return

        }

        let curIdx = e.target.idx

        let curCardItem: cc.Node = this.cardsContent.children[curIdx]

        if (curCardItem.val == null) {

            return

        }

        if (this._gameInfo.selectCards.indexOf(curCardItem) != -1) {

            this._gameInfo.selectCards = []

            this.endCardAction(curCardItem)

            return

        }

        this._gameInfo.selectCards.push(curCardItem)

        if (this._gameInfo.selectCards.length == 2) {

            if (this.tryDispel()) {

                let firstCard = this._gameInfo.selectCards.shift()

                let secondCard = this._gameInfo.selectCards.shift()

                this._gameInfo.data[firstCard.idx] = null

                this._gameInfo.data[secondCard.idx] = null

                this.setupCards()

                if (this.gameOver()) {

                    this.endTiming()

                    this._gameInfo.status = GAME_STATUS.END

                    this.winParticle.active = true

                    this.saveResult()

                    this.scheduleOnce(() => {

                        this.winParticle.active = false

                        this.init()

                    }, 5)
                }

            } else {

                this.errorCardAction()

            }

        } else {

            this.startCardAction()

        }

    }

    btnReturn() {

        if (this._gameInfo.playing) { return }

        this.node.active = false

    }

}